use super::time::{SysTime,Clocker,UClock,UClockData,UTime,SysConvertor,CpuClock};
use super::ls::{least_square,error};
use super::clk::{ClockTracer,ClockTrace};
use super::umutex::{ScopedLock,Umutex};
use super::pair::pair;
use std::thread;
use std::time::Duration;


#[derive(Debug)]
pub struct PLL {
    e : f64,
    convertor : UClockData,
    a :(f64,f64),
    scale : f64,
    h : bool,
}
#[repr(C)]
pub struct PLLExchange {
    data : (f64,f64),
    mutex : Umutex
}
impl PLL {
    pub fn new(data : &UClockData) -> Self {
        Self {
            e : 0f64,
            convertor : *data,
            a:(4e-11,4e-13),
            scale:1f64,
            h : false,
        }
    }
    pub fn get_a(&self) -> (f64,f64) { self.a }
    pub fn one_ls<T:Clocker+Default>(&mut self) -> UClock<T> {
        let mut tempc = T::default();
        let p = pair(tempc.now(),SysTime::now());
        self.update(p);
        thread::sleep(Duration::from_millis(100));
        UClock::<T>{ data: self.getdata(), clock:T::default()}
    }
    pub fn from_data(&mut self,d : &mut PLLExchange) {
        let _l = ScopedLock::new(&mut d.mutex);
        self.a = d.data;
    }
    pub fn to_data(a:(f64,f64),d : &mut PLLExchange) {
        let _l = ScopedLock::new(&mut d.mutex);
        d.data = a;
    }
    pub fn set_zero(&mut self,p:pair<UTime,SysTime>) { 
        self.convertor.self_start=p.0;
        self.convertor.cpu_start = p.1;
        self.e = 0f64; 
    }
    pub fn set_scale(&mut self,s:f64) { self.scale = s; }
    pub fn set_health(&mut self,h : bool) { self.h = h; } 
}

impl ClockTracer for PLL {
    fn update(&mut self,p :pair::<UTime,SysTime>) -> bool {
        let e = p.error(&self.convertor);
        if e*e > 1e8 {
            if !self.h {
                self.convertor.self_start.0 += e as i64;
                self.e = 0f64;
            }
            false
        } else {
            self.convertor.rate_nano += self.scale*((e-self.e)*self.a.0+e*self.a.1);
            let self_start = self.convertor.from_sys(&p.1);
            self.convertor.self_start = self_start;
            self.convertor.cpu_start = p.1;
            self.convertor.quality = e*e;
            self.e = e;
            true
        }
    }
    fn getdata(&self) -> UClockData {
        self.convertor.clone()
    }
    fn error(&self) -> Option<f64> { Some(self.e*self.e) }
}

#[derive(Debug)]
pub struct LSandPLL<T:ClockTracer+Default> {
    pub l0 : T,
    pub l1 : PLL,
}

impl<T:ClockTracer+Default> Default for LSandPLL<T> {
    fn default() -> Self {
        let l0 = T::default();
        let l1 = PLL::new(&l0.getdata());
        Self { l0, l1 }
    }
}

impl<T:ClockTracer+Default> LSandPLL<T> {
    pub fn set_filter(&mut self,a:&mut PLLExchange) {
        self.l1.from_data(a);
    }
    pub fn one_ls<C:Clocker+Default>(&mut self) -> Option<UClock<C>> {
        let mut tempc = C::default();
        let p = pair(tempc.now(),SysTime::now());
        if self.l0.update(p) {
            let d = self.l0.getdata();
            if (self.l1.convertor.rate_nano-d.rate_nano).abs() > 1e-7 {
                self.l1.set_zero(p);
            }
            self.l1.convertor.rate_nano = d.rate_nano;
            for _ in 0..60 {
                let p = pair(tempc.now(),SysTime::now());
                self.l1.update(p);
                thread::sleep(Duration::from_millis(100));
            }
            
            Some(UClock{ data: self.l1.getdata(), clock:C::default()})    
        } else {
            thread::sleep(Duration::from_millis(6000));
            None
        }
    }
}

pub type PLLex = LSandPLL<ClockTrace<CpuClock>>;